﻿/*!
 * \file http_srv.hpp
 * \date 2023/05/13 16:15
 *
 * \author Yunfeng.Bo
 * Contact: Yunfeng.Bo@qq.com
 *
 * \brief 基于 libhv 实现的 HTTP server
 *          使用 boost.json
 *          使用 boost.signals2
 *          使用 自定义的 erc 错误处理
 *          支持 http 和 websocket（可以同一端口服务）
 *
 *  定义 HTTP 服务对象：
 *      http_srv m_http_server;
 *  启动 HTTP 服务：
 *      int ret = m_http_server.start(8888);
 *  监听 HTTP 处理消息：
 *      m_http_server.on_request.connect([this](std::string_view method,
                                                std::string_view uri,
                                                std::string_view http_version) {
            ...
        });
        m_http_server.on_request.on_response ...
 *  注册服务函数，URI 需以 /api 开头：regist({Method...}, uri, {param1, ...}, func);
 *      server.regist({"POST"}, "/api/test_dll", {"method", "x", "y"}, &MainFrm::test_func, this);
 *  注册静态文件服务：
 *      m_http_server.regist_static("/log_file", "./");
 *      m_http_server.regist_static("/html", "./html");
 *  注册代理服务
 *      m_http_server.regist_proxy(uri, target);
 *
 * \note
 *      1. 注册的服务函数：
 *          - 可以是普通方法，也可以是类成员方法；
 *          - 方法的参数，可以是简单类型（string\int\bool\double），也可以是对象类型
 *          - 当某一参数在 query 和 body 中同时指定时，query 的优先级更高
            - query 中指定的参数，一律视为 std::string 类型，这里不做类型转换
 *          - 对象类型的方法参数，需实现 from_json 方法，以支持数据转换：
 *              virtual std::shared_ptr<http_rep> from_json(json::object& jo);
 *      2. 没有使用 日志 功能，所有的信息通过 boost.signals2 或 返回的错误信息（返回到客户端）发送
 *      3. 除基本的 API 调用和自定义协议服务外，服务器也提供了文件服务、代理服务等，
 *          不过这些纯属小项目里偷懒的玩法，正式的项目里，使用 nginx 等会获得更好的效果
 *
 *      - HTTP 只能注册基于 JSON 参数的方法调用
 *      - websocket 可以注册所有类型（基于 JSON 或 MsgPack）参数的方法调用
 *      - 参数类型是什么，需要与客户端做好约定
 *
 */

#pragma once

// 使用 boost sinals2 库，发送消息
#include <boost/signals2/signal.hpp>
namespace sig2 = boost::signals2;

// 使用 libhv
#ifdef _MSC_VER
#    pragma warning(push)
#    pragma warning(disable : 4201)
#    pragma warning(disable : 4819)
#else
#    pragma GCC diagnostic push
// #    pragma GCC diagnostic ignored "-Wsign-compare"
#endif
#include <hv/EventLoop.h>
#include <hv/HttpServer.h>
#include <hv/WebSocketServer.h>
#include <hv/hlog.h>
#include <hv/htime.h>
#ifdef _MSC_VER
#    pragma warning(pop)
#else
#    pragma GCC diagnostic pop
#endif

#include "http_rep.h"

template<typename T>
struct type_xx {
    typedef T type;
};

template<>
struct type_xx<void> {
    typedef int8_t type;
};

// 用 Tuple 做参数调用函数模板类，辅助
template<typename Function, typename Tuple, std::size_t... Index>
decltype(auto) invoke_impl(Function&& func, Tuple&& t, std::index_sequence<Index...>) {
    return func(std::get<Index>(std::forward<Tuple>(t))...);
}

// 用 Tuple 做参数调用函数模板类
template<typename Function, typename Tuple>
decltype(auto) invoke(Function&& func, Tuple&& t) {
    constexpr auto size = std::tuple_size<typename std::decay<Tuple>::type>::value;
    return invoke_impl(
        std::forward<Function>(func), std::forward<Tuple>(t), std::make_index_sequence<size>{});
}

// 用 Tuple 做参数调用服务方法
template<typename R, typename F, typename ArgsTuple>
typename std::enable_if<!std::is_same<R, void>::value, typename type_xx<R>::type>::type
call_helper(F f, ArgsTuple args) {
    return invoke(f, args);
}

// 基于 JSON 调用时，由于 JSON 非严格保证数据的顺序，因此需要对参数序列定义
typedef std::vector<std::string> params_def_t;

/**
 * @brief 内部管理用，类型定义：基于 JSON 的 HTTP server 服务方法的实现函数
 *          参数 JSON
 *          返回 http_rep
 */
typedef std::function<json_rep(const params_def_t&, json::value&)> json_srv_func;

/**
 * @brief 内部管理用，注册为 可被远程调用（RPC 基于 msgpack）的 服务函数
 *          第一个参数用于返回调用结果
 *          第二个参数是调用的参数
 */
typedef std::function<void(msgpack::serializer*, msgpack::serializer&)> rpc_func;

// 服务方法：参数定义 + 方法实现
struct http_srv_func {
    // 服务方法的参数定义（参数类型 + 参数名称，顺序相关）
    params_def_t  params_def;
    json_srv_func func;   // 服务方法的实现函数
};

// 从 JSON 对象中提取所需数据：简单数据
std::shared_ptr<json_rep> value_from_json(std::string& v, const std::string& name,
                                          json::object& jo);
std::shared_ptr<json_rep> value_from_json(int& v, const std::string& name, json::object& jo);
std::shared_ptr<json_rep> value_from_json(uint64_t& v, const std::string& name, json::object& jo);
std::shared_ptr<json_rep> value_from_json(bool& v, const std::string& name, json::object& jo);
std::shared_ptr<json_rep> value_from_json(double& v, const std::string& name, json::object& jo);
std::shared_ptr<json_rep> value_from_json(json::object& v, const std::string& name,
                                          json::object& jo);
// 从 JSON 对象中提取所需数据：对象类型数据（需提供 from_json 方法）
// 对于 容器类型 的数据，需要包一层，成为一个特定的，提供 from_json 方法 的对象，这样就可以处理了
template<typename T>
std::shared_ptr<json_rep> value_from_json(T& v, const std::string& name, json::object& jo) {
    auto jv = jo.if_contains(name);
    if (!jv)
        return std::make_shared<json_rep>(HTTP_FIELD_NOT_FOUND(name));   // 属性未找到
    if (!jv->is_object())
        return std::make_shared<json_rep>(HTTP_FIELD_TYPE_ERR(name, "object"));

    return v.from_json(jv->as_object());
}

// 根据 params_def[Index] 中的参数定义，从 json_args 中获取指定的数据，设置到元组 t 的第 Id 项中
template<typename Tuple, std::size_t Index>
std::shared_ptr<json_rep> get_value_by_index(const params_def_t& params_def, json::value& json_args,
                                             Tuple& t) {
    auto& jo = json_args.as_object();

    // 服务端方法注册错误，要求的参数，在注册的方法中没有指定
    if (Index >= params_def.size())
        return std::make_shared<json_rep>(HTTP_FUNC_REG_WRONG);

    auto def = params_def.at(Index);

    return value_from_json(std::get<Index>(t), def, jo);
}

template<typename Tuple, std::size_t... Is>
json_rep json_to_tuple(const params_def_t& params_def, json::value& json_args,
                       std::index_sequence<Is...>, Tuple&           t) {
#ifdef DEBUG   // DEBUG 版本下，参数定义数量和实际注册的函数参数数量不同时，返回错误
    // 判断一下参数定义和具体实现方法参数的数量是否匹配
    size_t args_tuple_size = std::tuple_size<typename std::decay<Tuple>::type>::value;
    size_t params_def_size = params_def.size();

    if (params_def_size != args_tuple_size)
        return HTTP_FUNC_PARAM_COUNT_WRONG(args_tuple_size, params_def_size);
#endif

    // 生成列表的过程，创建 tuple 作为函数参数，同时生成的结果（可能多个错误）放到列表中
    std::vector<std::shared_ptr<json_rep>> l = std::initializer_list<std::shared_ptr<json_rep>>{
        get_value_by_index<Tuple, Is>(params_def, json_args, t)...};

    // 不论有多少错误，都只保留一个，其它都清掉
    std::shared_ptr<json_rep> result;
    for (std::vector<std::shared_ptr<json_rep>>::iterator it = l.begin(); it != l.end(); it++) {
        if (nullptr == *it)
            continue;

        if ((*it)->operator int())   // 检查 code 的同时，会清除 active 状态
            result = *it;
    }

    // 如果全对，就返回成功，否则重新激活错误
    if (result) {
        json_rep r(*result);
        return r.reactivate();
    } else
        return HTTP_SUCCESS;
}

// 基于 JSON 参数的普通函数调用 step 1
template<typename F>
json_rep json_callproxy(F func, const params_def_t& params_def, json::value& json_args) {
    return json_callproxy_(func, params_def, json_args);
}

// 基于 JSON 参数的普通函数调用 step 2
template<typename R, typename... Args>
R json_callproxy_(R (*func)(Args...), const params_def_t& params_def, json::value& json_args) {
    return json_callproxy_(std::function<R(Args...)>(func), params_def, json_args);
}

// 基于 JSON 参数的普通函数调用 step 3
template<typename R, typename... Args>
R json_callproxy_(std::function<R(Args... args)> func, const params_def_t& params_def,
                  json::value& json_args) {
    using args_type = std::tuple<typename std::decay<Args>::type...>;

    args_type as;
    json_to_tuple<args_type>(params_def, json_args, std::index_sequence_for<Args...>{}, as);
    R ret = call_helper<R>(func, as);
    return ret;
}

// 基于 JSON 参数的类成员函数调用 step 1
template<typename F, typename S>
json_rep json_callproxy(F func, S* s, const params_def_t& params_def, json::value& json_args) {
    return json_callproxy_(func, s, params_def, json_args);
}

// 基于 JSON 参数的类成员函数调用 step 2
template<typename R, typename C, typename S, typename... Args>
R json_callproxy_(R (C::*func)(Args...), S* s, const params_def_t& params_def,
                  json::value& json_args) {
    using args_type = std::tuple<typename std::decay<Args>::type...>;

    args_type as;
    json_to_tuple<args_type>(params_def, json_args, std::index_sequence_for<Args...>{}, as);
    auto ff  = [=](Args... args) -> R { return (s->*func)(args...); };
    R    ret = call_helper<R>(ff, as);
    return ret;
}

// 基于 msgpack 参数的普通函数调用 step 1
template<typename F>
void mp_callproxy(F func, msgpack::serializer* pr, msgpack::serializer& sr_args) {
    mp_callproxy_(func, pr, sr_args);
}

// 基于 msgpack 参数的普通函数调用 step 2
template<typename R, typename... Args>
void mp_callproxy_(R (*func)(Args...), msgpack::serializer* pr, msgpack::serializer& sr_args) {
    mp_callproxy_(std::function<R(Args...)>(func), pr, sr_args);
}

// 基于 msgpack 参数的普通函数调用 step 3
template<typename R, typename... Args>
void mp_callproxy_(std::function<R(Args... args)> func, msgpack::serializer* pr,
                   msgpack::serializer& sr_args) {
    using args_type = std::tuple<typename std::decay<Args>::type...>;

    args_type as = sr_args.get_tuple<args_type>(std::index_sequence_for<Args...>{});

    typename type_xx<R>::type r = call_helper<R>(func, as);

    // response_t<R> response((int)rep_code::SUCCESS, "success", r);
    (*pr) << r;
}

// 基于 msgpack 参数的类成员函数调用 step 1
template<typename F, typename S>
void mp_callproxy(F func, S* s, msgpack::serializer* pr, msgpack::serializer& sr_args) {
    mp_callproxy_(func, s, pr, sr_args);
}

// 基于 msgpack 参数的类成员函数调用 step 1
template<typename R, typename C, typename S, typename... Args>
void mp_callproxy_(R (C::*func)(Args...), S* s, msgpack::serializer* pr,
                   msgpack::serializer& sr_args) {
    using args_type = std::tuple<typename std::decay<Args>::type...>;

    args_type as = sr_args.get_tuple<args_type>(std::index_sequence_for<Args...>{});

    auto                      ff = [=](Args... args) -> R { return (s->*func)(args...); };
    typename type_xx<R>::type r  = call_helper<R>(ff, as);

    // response_t<R> response((int)rep_code::SUCCESS, "success", r);
    (*pr) << r;
}

/*!
 * \class http_srv
 *
 * \brief HTTP server
 *
 * \author Yunfeng.Bo
 * \date 2023/05/13 16:26
 */
class http_srv {
public:
    explicit http_srv();
    virtual ~http_srv();

    // 启动 HTTP 服务
    int start(unsigned short port);
    // 停止 HTTP 服务
    void stop();

    std::string make_path(const std::string& method, const std::string& uri) {
        return method + ":" + uri;
    }

    const std::map<std::string, http_srv_func>& api_map_funcs() { return m_map_funcs; }
    const std::map<std::string, rpc_func>&      ws_map_funcs() { return m_map_ws_funcs; }

    // 注册基于 JSON 参数的服务方法（普通函数）
    template<typename F>
    void regist_api(const std::vector<http_method> methods, const std::string& name,
                    const params_def_t& params, F func) {
        assert(m_http_router);
        for (auto method : methods) {
            m_http_router->Handle(
                http_method_str(method),
                name.c_str(),
                [this](HttpRequest* req, HttpResponse* resp) { return http_handler(req, resp); });

            m_map_funcs[make_path(http_method_str(method), name)] = http_srv_func(
                {params,
                 std::bind(json_callproxy<F>, func, std::placeholders::_1, std::placeholders::_2)});
        }
    }

    // 注册基于 JSON 参数的服务方法（类成员函数）
    template<typename F, typename S>
    void regist_api(const std::vector<http_method> methods, const std::string& name,
                    const params_def_t& params, F func, S* s) {
        assert(m_http_router);
        for (auto method : methods) {
            m_http_router->Handle(
                http_method_str(method),
                name.c_str(),
                [this](HttpRequest* req, HttpResponse* resp) { return http_handler(req, resp); });

            m_map_funcs[make_path(http_method_str(method), name)] = http_srv_func(
                {params,
                 std::bind(
                     json_callproxy<F, S>, func, s, std::placeholders::_1, std::placeholders::_2)});
        }
    }

    // 与 regist 功能相对，注销一个远程方法
    void unregist(const std::string& name) { m_map_funcs.erase(name); }

    // 注册服务方法（普通函数），WebSocket 响应，基于任意类型参数的方法
    template<typename F>
    void regist_api(const std::string& name, F func) {
        m_map_ws_funcs[name] =
            std::bind(mp_callproxy<F>, func, std::placeholders::_1, std::placeholders::_2);
    }

    // 注册服务方法（类成员函数），WebSocket 响应，基于任意类型参数的方法
    template<typename F, typename S>
    void regist_api(const std::string& name, F func, S* s) {
        m_map_ws_funcs[name] =
            std::bind(mp_callproxy<F, S>, func, s, std::placeholders::_1, std::placeholders::_2);
    }

    void unregist_mp(const std::string& name) { m_map_ws_funcs.erase(name); }

    // 注册一个静态文件访问地址和根目录
    void regist_static(const std::string& uri, const std::string& root_dir) {
        if (!m_http_router)
            return;
        m_http_router->Static(uri.c_str(), root_dir.c_str());
    }

    // 注册代理服务地址
    void regist_proxy(const std::string& uri, const std::string& target) {
        if (!m_http_router)
            return;
        m_http_router->Proxy(uri.c_str(), target.c_str());
    }

public:
    // 信号：服务启动，参数为：服务端口，启动结果（0表示成功，其它值参考 workflow 文档）
    sig2::signal<void(unsigned short, int)> on_start;
    // 信号：服务停止
    sig2::signal<void()> on_stop;
    // 信号：收到新的 HTTP Request，参数分别是 method, uri
    sig2::signal<void(std::string_view, std::string_view)> on_request;
    // 信号：消息处理完毕
    sig2::signal<void(json_rep&)> on_response;
    // 信号：新的 websocket 客户端连接，参数为请求的路径（path）和自动生成的 客户端ID
    sig2::signal<void(const std::string&, uint32_t)> on_ws_open;
    // 信号：收到 websocket 信息
    sig2::signal<void(uint32_t, const std::string&)> on_ws_message;
    // 信号：响应 websocket 信息
    sig2::signal<void(uint32_t, uint32_t)> on_ws_response;
    // 信号：websocket 连接断开
    sig2::signal<void(uint32_t)> on_ws_close;

private:
    // HTTP 远程调用 服务方法入口
    int http_handler(HttpRequest* req, HttpResponse* resp);

    // WebSocket open 消息处理
    void ws_open(const WebSocketChannelPtr& channel, const HttpRequestPtr& req);
    void ws_message(const WebSocketChannelPtr& channel, const std::string& msg);
    void ws_close(const WebSocketChannelPtr& channel);

private:
    // 基于 libhv 的 HTTP service
    std::shared_ptr<HttpService> m_http_router;
    // 基于 libhv 的 HTTP websocket service
    std::shared_ptr<WebSocketService> m_ws_router;
    // 基于 libhv 的 http server
    std::shared_ptr<http_server_t> m_http_server;

    // 服务名（Method + URI） => 服务处理函数 map
    std::map<std::string, http_srv_func> m_map_funcs;

    // 服务名（Method） => WebSocket 服务处理函数 map
    std::map<std::string, rpc_func> m_map_ws_funcs;
};